{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## For else\n",
    "\n",
    "在 python 中，for … else 表示这样的意思，for 中的语句和普通的没有区别，else 中的语句会在循环正常执行完（即 for 不是通过 break 跳出而中断的）的情况下执行，while … else 也是一样。 \n",
    "\n",
    "原文：for loops also have an else clause which most of us are unfamiliar with. The else clause executes after the loop completes normally. This means that the loop did not encounter a break statement. They are really useful once you understand where to use them. I, myself, came to know about them a lot later.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "2\n",
      "0\n",
      "4 equals 2 * 2.0\n",
      "6 equals 2 * 3.0\n",
      "8 equals 2 * 4.0\n",
      "9 equals 3 * 3.0\n",
      "2 is a prime number\n",
      "3 is a prime number\n",
      "4 equals 2 * 2.0\n",
      "5 is a prime number\n",
      "6 equals 2 * 3.0\n",
      "7 is a prime number\n",
      "8 equals 2 * 4.0\n",
      "9 equals 3 * 3.0\n"
     ]
    }
   ],
   "source": [
    "for  i in range(3):\n",
    "    if i == 2:\n",
    "        print(i)\n",
    "        break\n",
    "\n",
    "else:\n",
    "    print(0)\n",
    "\n",
    "for i in range(3):\n",
    "    if i == 2:\n",
    "        print(i)\n",
    "else:\n",
    "    print(0)\n",
    "\n",
    "\n",
    "for n in range(2, 10):\n",
    "    for x in range(2, n):\n",
    "        if n % x == 0:\n",
    "            print(n, 'equals', x, '*', n/x)\n",
    "            break\n",
    "\n",
    "for n in range(2, 10):\n",
    "    for x in range(2, n):\n",
    "        if n % x == 0:\n",
    "            print( n, 'equals', x, '*', n/x)\n",
    "            break\n",
    "    else:\n",
    "        # loop fell through without finding a factor\n",
    "        print(n, 'is a prime number')      "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  Try except else\n",
    "    ```\n",
    "    try:\n",
    "    <语句>        #运行别的代码\n",
    "    except <名字>：\n",
    "    <语句>        #如果在try部份引发了'name'异常\n",
    "    except <名字>，<数据>:\n",
    "    <语句>        #如果引发了'name'异常，获得附加的数据\n",
    "    else:\n",
    "    <语句>        #如果没有异常发生\n",
    "    ```\n",
    "    * try的工作原理是，当开始一个try语句后，python就在当前程序的上下文中作标记，这样当异常出现时就可以回到这里，try子句先执行，接下来会发生什么依赖于执行时是否出现异常。\n",
    "        + 如果当try后的语句执行时发生异常，python就跳回到try并执行第一个匹配该异常的except子句，异常处理完毕，控制流就通过整个try语句（除非在处理异常时又引发新的异常）。\n",
    "        + 如果在try后的语句里发生了异常，却没有匹配的except子句，异常将被递交到上层的try，或者到程序的最上层（这样将结束程序，并打印默认的出错信息）。\n",
    "        + 如果在try子句执行时没有发生异常，python将执行else语句后的语句（如果有else的话），然后控制流通过整个try语句。\n",
    "    try的工作原理是，当开始一个try语句后，python就在当前程序的上下文中作标记，这样当异常出现时就可以回到这里，try子句先执行，接下来会发生什么依赖于执行时是否出现异常。\n",
    "        + 如果当try后的语句执行时发生异常，python就跳回到try并执行第一个匹配该异常的except子句，异常处理完毕，控制流就通过整个try语句（除非在处理异常时又引发新的异常）。\n",
    "        如果在try后的语句里发生了异常，却没有匹配的except子句，异常将被递交到上层的try，或者到程序的最上层（这样将结束程序，并打印默认的出错信息）。\n",
    "        + 如果在try子句执行时没有发生异常，python将执行else语句后的语句（如果有else的话），然后控制流通过整个try语句。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "3\n",
      "内容写入文件成功\n",
      "Always to be excuted!\n",
      "Enter your age: 25\n",
      "Welcome, you are old enough.\n"
     ]
    }
   ],
   "source": [
    "def A():\n",
    "    try:\n",
    "        print(1)\n",
    "        a=3//0\n",
    "    except:\n",
    "        print(2)\n",
    "        return\n",
    "    finally:\n",
    "        print(3)\n",
    "    print(4)\n",
    "    return\n",
    "\n",
    "def B():   \n",
    "    try:\n",
    "        fh = open(\"testfile\", \"w\")\n",
    "        fh.write(\"这是一个测试文件，用于测试异常!!\")\n",
    "    except IOError:\n",
    "        print (\"Error: 没有找到文件或读取文件失败\")\n",
    "    else:\n",
    "        print (\"内容写入文件成功\")\n",
    "        fh.close()\n",
    "    finally:\n",
    "        print(\"Always to be excuted!\")\n",
    "\n",
    "A()\n",
    "B()\n",
    "\n",
    "try:\n",
    "    age=int(input('Enter your age: '))\n",
    "except:\n",
    "    print ('You have entered an invalid value.')\n",
    "else:\n",
    "    if age <= 21:\n",
    "        print('You are not allowed to enter, you are too young.')\n",
    "    else:\n",
    "        print('Welcome, you are old enough.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  Try finally return\n",
    "    原文：\n",
    "    A finally clause is always executed before leaving the try statement, whether an exception has occurred or not. When an exception has occurred in the try clause and has not been handled by an except clause (or it has occurred in a except or else clause), it is re-raised after the finally clause has been executed. The finally clause is also executed “on the way out” when any other clause of the try statement is left via a break, continue or return statement. A more complicated example (having except and finally clauses in the same try statement works as of Python 2.5):\n",
    "    So once the try/except block is left using return, which would set the return value to given - finally blocks will always execute, and should be used to free resources etc. while using there another return - overwrites the original one."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "def func1():\n",
    "    try:\n",
    "        return 1\n",
    "    finally:\n",
    "        return 2\n",
    "\n",
    "def func2():\n",
    "    try:\n",
    "        raise ValueError()\n",
    "    except:\n",
    "        return 1\n",
    "    finally:\n",
    "        return 3\n",
    "\n",
    "print(func1())\n",
    "print(func2())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 上面 except、except X、else是可选项，但是：\n",
    "\n",
    "    在上面展示的完整语句中try/ except/ else/ finally所出现的顺序是try-->except X-->except-->else-->finally。else和finally如果存在的话，else必须在finally之前，finally必须在整个程序的最后。\n",
    "    \n",
    "    **else**的存在必须以except或except X存在为前提，如果没有except而在tryblock中使用esle的话，会出现语法错误。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 装饰器\n",
    "    A decorator is the name used for a software design pattern. Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated. \n",
    "    参考：https://wiki.python.org/moin/PythonDecorators\n",
    "        https://realpython.com/primer-on-python-decorators/\n",
    "        https://www.cnblogs.com/serpent/p/9445592.html\n",
    "        https://www.runoob.com/w3cnote/python-func-decorators.html\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 内置装饰器\n",
    "    参考: https://docs.python.org/3/library/functions.html\n",
    "          https://blog.csdn.net/felix_yujing/article/details/79749944\n",
    "    @classmethod 类方法的第一个参数是一个类，是将类本身作为操作的方法。类方法被哪个类调用，就传入哪个类作为第一个参数进行操作。\n",
    "    @property 使调用类中的方法像引用类中的字段属性一样。被修饰的特性方法，内部可以实现处理逻辑，但对外提供统一的调用方式。\n",
    "    @staticmethod 将类中的方法装饰为静态方法，即类不需要创建实例的情况下，可以通过类名直接引用。到达将函数功能与实例解绑的效果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "通过实例引用属性\n",
      "felix\n",
      "像引用属性一样调用@property修饰的方法\n",
      "hello felix\n"
     ]
    }
   ],
   "source": [
    "# @property 使调用类中的方法像引用类中的字段属性一样。被修饰的特性方法，内部可以实现处理逻辑，但对外提供统一的调用方式。\n",
    "# coding: utf-8\n",
    "class TestClass:\n",
    "    name = \"test\"\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "    @property\n",
    "    def sayHello(self):\n",
    "        print (\"hello\", self.name)\n",
    "\n",
    "cls = TestClass(\"felix\")\n",
    "print (\"通过实例引用属性\")\n",
    "print (cls.name)\n",
    "print (\"像引用属性一样调用@property修饰的方法\")\n",
    "cls.sayHello"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "通过实例引用方法\n",
      "5\n",
      "类名直接引用静态方法\n",
      "5\n",
      "15\n"
     ]
    }
   ],
   "source": [
    "# @staticmethod 将类中的方法装饰为静态方法，即类不需要创建实例的情况下，可以通过类名直接引用。到达将函数功能与实例解绑的效果。\n",
    "# coding: utf-8\n",
    "class TestClass:\n",
    "    name = \"test\"\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "    @staticmethod\n",
    "    def fun(self, x, y):\n",
    "        return  x + y\n",
    "\n",
    "cls = TestClass(\"felix\")\n",
    "print (\"通过实例引用方法\")\n",
    "print (cls.fun(None, 2, 3))  # 参数个数必须与定义中的个数保持一致，否则报错\n",
    "\n",
    "print (\"类名直接引用静态方法\")\n",
    "print (TestClass.fun(None, 2, 3)) # 参数个数必须与定义中的个数保持一致，否则报错\n",
    "print(TestClass.fun(5,6,9))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "通过实例调用\n",
      "Normal car of BMW\n",
      "通过类名直接调用\n",
      "SUV car of Benz\n",
      "audi car of audi\n"
     ]
    }
   ],
   "source": [
    "# @classmethod 类方法的第一个参数是一个类，是将类本身作为操作的方法。类方法被哪个类调用，就传入哪个类作为第一个参数进行操作。\n",
    "# coding: utf-8\n",
    "class Car(object):\n",
    "    car = \"audi\"\n",
    "\n",
    "    @classmethod\n",
    "    def value(self, category): # 可定义多个参数，但第一个参数为类本身\n",
    "        print (\"%s car of %s\" % (category, self.car))\n",
    "\n",
    "class BMW(Car):\n",
    "    car = \"BMW\"\n",
    "\n",
    "class Benz(Car):\n",
    "    car = \"Benz\"\n",
    "\n",
    "print (\"通过实例调用\")\n",
    "baoma = BMW()\n",
    "baoma.value(\"Normal\") # 由于第一个参数为类本身，调用时传入的参数对应的时category\n",
    "\n",
    "print (\"通过类名直接调用\")\n",
    "Benz.value(\"SUV\")\n",
    "\n",
    "Car.value(\"audi\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  装饰器基本用法\n",
    "    装饰器参数传递\n",
    "    @装饰器后有参数时\n",
    "    两个装饰器同时修饰一个函数（重点看执行顺序）\n",
    "    装饰器类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 120,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "现在开始装饰\n",
      "我是被装饰的函数\n",
      "现在结束装饰\n"
     ]
    }
   ],
   "source": [
    "#  简单装饰器\n",
    "def log_time(func):  # 此函数的作用时接受被修饰的函数的引用test，然后被内部函数使用\n",
    "    def make_decorater():\n",
    "        print('现在开始装饰')\n",
    "        func()\n",
    "        print('现在结束装饰')\n",
    "    return make_decorater  # log_time()被调用后，运行此函数返回make_decorater()函数的引用make_decorater\n",
    " \n",
    "@log_time  # 此行代码等同于，test=log_time(test)=make_decorater\n",
    "def test():\n",
    "    print('我是被装饰的函数')\n",
    "test()  # test()=make_decorater()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 124,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Now decorater\n",
      "I am here .\n",
      "Fininshed decorater\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 124,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 被装饰的函数有形参时\n",
    "def log_time(func):\n",
    "    def make_decorater(*argvs,**kargvs):  # 接受调用语句的实参，在下面传递给被装饰函数（原函数）\n",
    "        print('Now decorater')\n",
    "        tmp = func(argvs[0]) # 如果在这里return，则下面的代码无法执行，所以引用并在下面返回\n",
    "        print('Fininshed decorater') \n",
    "        return tmp\n",
    "    return make_decorater # 因为被装饰函数里有return，所以需要给调用语句（test（2））一个返回\n",
    "\n",
    "@log_time\n",
    "def test(num):\n",
    "    print('I am here .')\n",
    "    return num + 1\n",
    "\n",
    "test(9)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 126,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('index.html/',) {}\n",
      "现在开始装饰\n",
      "我是被装饰的函数\n",
      "现在结束装饰\n"
     ]
    }
   ],
   "source": [
    "# @装饰器后有参数时\n",
    "def get_parameter(*args,**kwargs):  # 工厂函数，用来接受@get_parameter('index.html/')的'index.html/'\n",
    "    def log_time(func):\n",
    "        def make_decorater():\n",
    "            print(args,kwargs)\n",
    "            print('现在开始装饰')\n",
    "            func()\n",
    "            print('现在结束装饰')\n",
    "        return make_decorater\n",
    "    return log_time\n",
    " \n",
    "@get_parameter('index.html/')\n",
    "def test():\n",
    "    print('我是被装饰的函数')\n",
    "    # return num+1\n",
    " \n",
    "test()  # test()=make_decorater()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 129,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1现在开始装饰\n",
      "2现在开始装饰\n",
      "我是被装饰的函数\n",
      "2现在结束装饰\n",
      "1现在结束装饰\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "#  两个装饰器同时修饰一个函数（重点看执行顺序）\n",
    "def log_time1(func):\n",
    "    def make_decorater(*args,**kwargs): \n",
    "        print('1现在开始装饰')\n",
    "        test_func = func(args[0],**kwargs) \n",
    "        print('1现在结束装饰') \n",
    "        return test_func \n",
    "    return make_decorater\n",
    " \n",
    "def log_time2(func):\n",
    "    def make_decorater(*args,**kwargs):  # 接受调用语句的实参，在下面传递给被装饰函数（原函数）\n",
    "        print('2现在开始装饰')\n",
    "        test_func = func(*args,**kwargs)  # 如果在这里return，则下面的代码无法执行，所以引用并在下面返回\n",
    "        print('2现在结束装饰')\n",
    "        return test_func  # 因为被装饰函数里有return，所以需要给调用语句（test（2））一个返回，又因为test_func = func(*args,**kwargs)已经调用了被装饰函数，这里就不用带（）调用了，区别在于运行顺序的不同。\n",
    "    return make_decorater\n",
    " \n",
    "@log_time1\n",
    "@log_time2\n",
    "def test(num):\n",
    "    print('我是被装饰的函数')\n",
    "    return num+1\n",
    " \n",
    "a = test(2)  # test(2)=make_decorater(2)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 重写了我们函数的名字和注释文档(docstring)。幸运的是Python提供给我们一个简单的函数来解决这个问题，那就是functools.wraps。我\n",
    "# @wraps接受一个函数来进行装饰，并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。\n",
    "\n",
    "from functools import wraps\n",
    "def decorator_name(f):\n",
    "    @wraps(f)\n",
    "    def decorated(*args, **kwargs):\n",
    "        if not can_run:\n",
    "            return \"Function will not run\"\n",
    "        return f(*args, **kwargs)\n",
    "    return decorated\n",
    " \n",
    "@decorator_name\n",
    "def func():\n",
    "    return(\"Function is running\")\n",
    " \n",
    "can_run = True\n",
    "print(func())\n",
    "print(func.__name__)\n",
    "# Output: Function is running\n",
    " \n",
    "can_run = False\n",
    "print(func())\n",
    "# Output: Function will not run\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 带参数的装饰器——在函数中嵌入装饰器\n",
    "    来想想这个问题，难道@wraps不也是个装饰器吗？但是，它接收一个参数，就像任何普通的函数能做的那样。那么，为什么我们不也那样做呢？ 这是因为，当你使用@my_decorator语法时，你是在应用一个以单个函数作为参数的一个包裹函数。Python里每个东西都是一个对象，而且包括函数！我们可以编写一下能返回一个包裹函数的函数。在函数中嵌入装饰器\n",
    "\n",
    "    我们回到日志的例子，并创建一个包裹函数，能让我们指定一个用于输出的日志文件。\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from functools import wraps\n",
    " \n",
    "def logit(logfile='out.log'):\n",
    "    def logging_decorator(func):\n",
    "        @wraps(func)\n",
    "        def wrapped_function(*args, **kwargs):\n",
    "            log_string = func.__name__ + \" was called\"\n",
    "            print(log_string)\n",
    "            # 打开logfile，并写入内容\n",
    "            with open(logfile, 'a') as opened_file:\n",
    "                # 现在将日志打到指定的logfile\n",
    "                opened_file.write(log_string + '\\n')\n",
    "            return func(*args, **kwargs)\n",
    "        return wrapped_function\n",
    "    return logging_decorator\n",
    " \n",
    "@logit()\n",
    "def myfunc1():\n",
    "    pass\n",
    " \n",
    "myfunc1()\n",
    "# Output: myfunc1 was called\n",
    "# 现在一个叫做 out.log 的文件出现了，里面的内容就是上面的字符串\n",
    " \n",
    "@logit(logfile='func2.log')\n",
    "def myfunc2():\n",
    "    pass\n",
    " \n",
    "myfunc2()\n",
    "# Output: myfunc2 was called\n",
    "# 现在一个叫做 func2.log 的文件出现了，里面的内容就是上面的字符串"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 装饰器类 ,定制__call__方法\n",
    "    现在我们有了能用于正式环境的logit装饰器，但当我们的应用的某些部分还比较脆弱时，异常也许是需要更紧急关注的事情。比方说有时你只想打日志到一个文件。而有时你想把引起你注意的问题发送到一个email，同时也保留日志，留个记录。这是一个使用继承的场景，但目前为止我们只看到过用来构建装饰器的函数。\n",
    "\n",
    "    幸运的是，类也可以用来构建装饰器。那我们现在以一个类而不是一个函数的方式，来重新构建logit。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 装饰器类，\n",
    "from functools import wraps\n",
    " \n",
    "class logit(object):\n",
    "    def __init__(self, logfile='out.log'):\n",
    "        self.logfile = logfile\n",
    " \n",
    "    def __call__(self, func):\n",
    "        @wraps(func)\n",
    "        def wrapped_function(*args, **kwargs):\n",
    "            log_string = func.__name__ + \" was called\"\n",
    "            print(log_string)\n",
    "            # 打开logfile并写入\n",
    "            with open(self.logfile, 'a') as opened_file:\n",
    "                # 现在将日志打到指定的文件\n",
    "                opened_file.write(log_string + '\\n')\n",
    "            # 现在，发送一个通知\n",
    "            self.notify()\n",
    "            return func(*args, **kwargs)\n",
    "        return wrapped_function\n",
    " \n",
    "    def notify(self):\n",
    "        # logit只打日志，不做别的\n",
    "        print(\"I just print the log !\")\n",
    "        pass\n",
    "\n",
    "#  类继承\n",
    "# 我们给 logit 创建子类，来添加 email 的功能(虽然 email 这个话题不会在这里展开)。\n",
    "# \n",
    "\n",
    "class email_logit(logit):\n",
    "    '''\n",
    "    一个logit的实现版本，可以在函数调用时发送email给管理员\n",
    "    '''\n",
    "    def __init__(self, email='admin@myproject.com', *args, **kwargs):\n",
    "        self.email = email\n",
    "        super(email_logit, self).__init__(*args, **kwargs) # 使用super 调用父类的方法\n",
    " \n",
    "    def notify(self):\n",
    "        # 发送一封email到self.email\n",
    "        # 这里就不做实现了\n",
    "        print(\"发送 log 到指定邮箱\")\n",
    "        pass\n",
    "    \n",
    "    \n",
    "# if __name__ == \"__main__\":\n",
    "@logit()\n",
    "def myfunc1():\n",
    "    print(\"使用装饰器类，打印LOG\")\n",
    "    pass\n",
    "\n",
    "myfunc1()\n",
    "\n",
    "# @email_logit 将会和 @logit 产生同样的效果，但是在打日志的基础上，还会多发送一封邮件给管理员。\n",
    "@email_logit()\n",
    "def myfunc2():\n",
    "    print(\"通过类继承扩展功能\")\n",
    "    \n",
    "myfunc2()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 装饰器基础使用场景 \n",
    "    授权(Authorization)\n",
    "    日志(Logging)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#  授权(Authorization)\n",
    "# 装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中：\n",
    "\n",
    "from functools import wraps\n",
    " \n",
    "def requires_auth(f):\n",
    "    @wraps(f)\n",
    "    def decorated(*args, **kwargs):\n",
    "        auth = request.authorization\n",
    "        if not auth or not check_auth(auth.username, auth.password):\n",
    "            authenticate()\n",
    "        return f(*args, **kwargs)\n",
    "    return decorated\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#  日志(Logging)\n",
    "# 日志是装饰器运用的另一个亮点。这是个例子：\n",
    "\n",
    "from functools import wraps\n",
    " \n",
    "def logit(func):\n",
    "    @wraps(func)\n",
    "    def with_logging(*args, **kwargs):\n",
    "        print(func.__name__ + \" was called\")\n",
    "        return func(*args, **kwargs)\n",
    "    return with_logging\n",
    " \n",
    "@logit\n",
    "def addition_func(x):\n",
    "   \"\"\"Do some math.\"\"\"\n",
    "   return x + x\n",
    " \n",
    " \n",
    "result = addition_func(4)\n",
    "# Output: addition_func was called\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## super() 函数\n",
    "    super() 函数是用于调用父类(超类)的一个方法。\n",
    "    super 是用来解决多重继承问题的，直接用类名调用父类方法在使用单继承的时候没问题，但是如果使用多继承，会涉及到查找顺序（MRO）、重复调用（钻石继承）等种种问题。\n",
    "    MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。\n",
    "**语法**\n",
    "\n",
    "super(type[, object-or-type])\n",
    "\n",
    "**参数**\n",
    "\n",
    "    type -- 类。\n",
    "    object-or-type -- 类，一般是 self\n",
    "\n",
    "*Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# python3 示例\n",
    "class A:\n",
    "     def add(self, x):\n",
    "         y = x+1\n",
    "         print(y)\n",
    "class B(A):\n",
    "    def add(self, x):\n",
    "        super().add(x)\n",
    "b = B()\n",
    "b.add(2)  # 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# python2示例\n",
    "#!/usr/bin/python\n",
    "# -*- coding: UTF-8 -*-\n",
    " \n",
    "class A(object):   # Python2.x 记得继承 object\n",
    "    def add(self, x):\n",
    "         y = x+1\n",
    "         print(y)\n",
    "class B(A):\n",
    "    def add(self, x):\n",
    "        super(B, self).add(x)\n",
    "b = B()\n",
    "b.add(2)  # 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 解释示例\n",
    "#!/usr/bin/python\n",
    "# -*- coding: UTF-8 -*-\n",
    " \n",
    "class FooParent(object):\n",
    "    def __init__(self):\n",
    "        self.parent = 'I\\'m the parent.'\n",
    "        print ('Parent')\n",
    "    \n",
    "    def bar(self,message):\n",
    "        print (\"%s from Parent\" % message)\n",
    " \n",
    "class FooChild(FooParent):\n",
    "    def __init__(self):\n",
    "        # super(FooChild,self) 首先找到 FooChild 的父类（就是类 FooParent），然后把类 FooChild 的对象转换为类 FooParent 的对象\n",
    "        super(FooChild,self).__init__()    \n",
    "        print ('Child')\n",
    "        \n",
    "    def bar(self,message):\n",
    "        super(FooChild, self).bar(message)\n",
    "        print ('Child bar fuction')\n",
    "        print (self.parent)\n",
    " \n",
    "if __name__ == '__main__':\n",
    "    fooChild = FooChild()\n",
    "    fooChild.bar('HelloWorld')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## sys 参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "argv[0]:/usr/lib/python3.6/site-packages/ipykernel_launcher.py,argv[1]:-f,argv[2]:/root/.local/share/jupyter/runtime/kernel-721d4cdc-bb76-441a-a047-c98daee2fa77.json\n"
     ]
    }
   ],
   "source": [
    "from sys import argv\n",
    "print(f\"argv[0]:{argv[0]},argv[1]:{argv[1]},argv[2]:{argv[2]}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 泛化参数组  \\*args 和 \\*\\*kwargs\n",
    "\n",
    "args 是 arguments 的缩写，表示位置参数；kwargs 是 keyword arguments 的缩写，表示关键字参数。这其实就是 Python 中可变参数的两种形式，并且 *args 必须放在 **kwargs 的前面，因为位置参数在关键字参数的前面。args负责的是位置参数，并放到了一个tuple中。而kwargs负责的是关键字参数，放到了一个dict中，通过key-value的方式存储。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Required argument:  1\n",
      "<class 'tuple'>\n",
      "Optional argument:  2\n",
      "Optional argument:  3\n",
      "Optional argument:  4\n",
      "<class 'tuple'>\n",
      "(2, 3)\n",
      "<class 'tuple'>\n",
      "(1, 2)\n"
     ]
    }
   ],
   "source": [
    "# *args就是就是传递一个可变参数列表给函数实参，这个参数列表的数目未知，甚至长度可以为0。\n",
    "def test_args(first, *args):\n",
    "    print('Required argument: ', first)\n",
    "    print(type(args))\n",
    "    for v in args:\n",
    "        print ('Optional argument: ', v)\n",
    "\n",
    "test_args(1, 2, 3, 4)\n",
    "\n",
    "def f(a,*other):\n",
    "    print (type(other))\n",
    "    print (other)\n",
    "    \n",
    "f(1,2,3)\n",
    "a=[1,2]\n",
    "f(3,*a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Required argument:  1\n",
      "<class 'dict'>\n",
      "Optional argument (args):  2\n",
      "Optional argument (args):  3\n",
      "Optional argument (args):  4\n",
      "Optional argument k1 (kwargs): 5\n",
      "Optional argument k2 (kwargs): 6\n"
     ]
    }
   ],
   "source": [
    "# **kwargs则是将一个可变的关键字参数的字典传给函数实参，同样参数列表长度可以为0或为其他值。\n",
    "def test_kwargs(first, *args, **kwargs):\n",
    "   print('Required argument: ', first)\n",
    "   print(type(kwargs))\n",
    "   for v in args:\n",
    "      print ('Optional argument (args): ', v)\n",
    "   for k, v in kwargs.items():\n",
    "      print ('Optional argument %s (kwargs): %s' % (k, v))\n",
    "\n",
    "test_kwargs(1, 2, 3, 4, k1=5, k2=6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "## __slots__\n",
    "    告诉Python不要使用字典，而且只给一个固定集合的属性分配空间。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 在Python中，每个类都有实例属性。默认情况下Python用一个字典来保存一个对象的实例属性。这个字典浪费了很多内存。\n",
    "# Python不能在对象创建时直接分配一个固定量的内存来保存所有的属性。\n",
    "# 如果你创建许多对象（我指的是成千上万个），它会消耗掉很多内存。\n",
    "\n",
    "# 不实用 __slots__\n",
    "class MyClass(object):\n",
    "    def __init__(self, name, identifier):\n",
    "        self.name = name\n",
    "        self.identifier = identifier\n",
    "        self.set_up()\n",
    "        \n",
    "# 使用 __slots__        \n",
    "class MyClass(object):\n",
    "    __slots__ = ['name', 'identifier']\n",
    "    def __init__(self, name, identifier):\n",
    "      self.name = name\n",
    "      self.identifier = identifier\n",
    "      self.set_up()\n",
    "    \n",
    "# 第二段代码会为你的内存减轻负担。通过这个技巧，有些人已经看到内存占用率几乎40%~50%的减少。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用ipython_memory_usage 查看内存占用\n",
    "import ipython_memory_usage.ipython_memory_usage as imu\n",
    "imu.start_watching_memory()\n",
    "%cat slots.py # 将之上代码段保存为独立.py并查看"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 全局，局部作用域 global,nonlocal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "gcount = 0\n",
    "\n",
    "# def global_test():\n",
    "#     gcount+=1\n",
    "#     print (gcount)\n",
    "# global_test()\n",
    "\n",
    "def global_test():\n",
    "    global gcount\n",
    "    gcount +=1\n",
    "    print(gcount)\n",
    "global_test()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 特殊的赋值，表单时判断和输入输出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n"
     ]
    }
   ],
   "source": [
    "# x,y,z = 0\n",
    "# (x,y,z) = 0\n",
    "# x = (y = z = 4)\n",
    "# x = (y=z) = 3\n",
    "x,y,z =1,2,3\n",
    "x,y = y,x \n",
    "x = y = z = 2\n",
    "print(x > y or z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "False\n",
      "False\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "print(any([1,2,9,4,5,0]))\n",
    "print(any([0,0,False,]))\n",
    "print(all([0,0,0,None]))\n",
    "print(all({14,1,2,3,3,4}))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Copy 浅拷贝，深拷贝\n",
    "    参考：https://docs.python.org/3.6/library/copy.html\n",
    "    The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or class instances):\n",
    "\n",
    "    A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.\n",
    "    A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.\n",
    "\n",
    "    * Two problems often exist with deep copy operations that don’t exist with shallow copy operations:\n",
    "\n",
    "    Recursive objects (compound objects that, directly or indirectly, contain a reference to themselves) may cause a recursive loop.\n",
    "    Because deep copy copies everything it may copy too much, such as data which is intended to be shared between copies.\n",
    "\n",
    "    * The deepcopy() function avoids these problems by:\n",
    "\n",
    "    keeping a memo dictionary of objects already copied during the current copying pass; and\n",
    "    letting user-defined classes override the copying operation or the set of components copied.\n",
    "    \n",
    "    This module does not copy types like module, method, stack trace, stack frame, file, socket, window, array, or any similar types. It does “copy” functions and classes (shallow and deeply), by returning the original object unchanged; this is compatible with the way these are treated by the pickle module.\n",
    "\n",
    "    Shallow copies of dictionaries can be made using dict.copy(), and of lists by assigning a slice of the entire list, for example, copied_list = original_list[:].\n",
    "\n",
    "    Classes can use the same interfaces to control copying that they use to control pickling. See the description of module pickle for information on these methods. In fact, the copy module uses the registered pickle functions from the copyreg module.\n",
    "\n",
    "    In order for a class to define its own copy implementation, it can define special methods __copy__() and __deepcopy__(). The former is called to implement the shallow copy operation; no additional arguments are passed. The latter is called to implement the deep copy operation; it is passed one argument, the memo dictionary. If the __deepcopy__() implementation needs to make a deep copy of a component, it should call the deepcopy() function with the component as first argument and the memo dictionary as second argument."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "对于不可 变类型 Number String Tuple,浅复制仅仅是地址指向，不会开辟新空间拷贝值\n",
      "num1:139789555205824\n",
      "num2:139789555205824\n",
      "====================\n",
      "对于可变类型 List、Dictionary、Set，浅复制会开辟新的空间地址(仅仅是最顶层开辟了新的空间)，进行浅拷贝\n",
      "list2:139789186523848 [11, 12, 33]\n",
      "list2:139789186184136 [11, 12]\n",
      "set1:139789195853384\n",
      "set2:139789195852936\n"
     ]
    }
   ],
   "source": [
    "# 浅拷贝\n",
    "\n",
    "#     1、对于 不可 变类型 Number String Tuple,浅复制仅仅是地址指向，不会开辟新空间。\n",
    "#     2、对于 可 变类型 List、Dictionary、Set，浅复制会开辟新的空间地址(仅仅是最顶层开辟了新的空间，里层的元素地址还是一样的)，进行浅拷贝\n",
    "#     3、浅拷贝后，改变原始对象中为可变类型的元素的值，会同时影响拷贝对象的；改变原始对象中为不可变类型的元素的值，只有原始类型受影响。 （操作拷贝对象对原始对象的也是同理）\n",
    "\n",
    "import copy\n",
    "\n",
    "# 不可变类型 Number String Tuple\n",
    "print(\"对于不可 变类型 Number String Tuple,浅复制仅仅是地址指向，不会开辟新空间拷贝值\")\n",
    "num1 = 17\n",
    "num2 = copy.copy(num1)\n",
    "print(\"num1:\" + str(id(num1)))\n",
    "print(\"num2:\" + str(id(num1)))\n",
    "# num1和num2的地址都相同\n",
    "\n",
    "print(\"=\"*20)\n",
    "print(\"对于可变类型 List、Dictionary、Set，浅复制会开辟新的空间地址(仅仅是最顶层开辟了新的空间)，进行浅拷贝\")\n",
    "\n",
    "list1 = [11,12]\n",
    "list2 = copy.copy(list1)\n",
    "list1.append(33)\n",
    "print(\"list2:\" + str(id(list1)),list1)\n",
    "print(\"list2:\" + str(id(list2)),list2)\n",
    "# list1和list2的地址不相同\n",
    "\n",
    "set1 = [{\"AA\",\"BB\"},1,2,3]\n",
    "set2 = copy.copy(set1)\n",
    "set1.append()\n",
    "print(\"set1:\" + str(id(set1)))\n",
    "print(\"set2:\" + str(id(set2)))\n",
    "# set1和set2的地址不相同"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 深拷贝\n",
    "\n",
    "#     1、浅拷贝，除了顶层拷贝，还对子元素也进行了拷贝（本质上递归浅拷贝）\n",
    "#     2、经过深拷贝后，原始对象和拷贝对象所有的子元素地址都是独立的了\n",
    "#     3、可以用分片表达式进行深拷贝\n",
    "#     4、字典的copy方法可以拷贝一个字典\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## help()和dir()函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function copy in module copy:\n",
      "\n",
      "copy(x)\n",
      "    Shallow copy operation on arbitrary Python objects.\n",
      "    \n",
      "    See the module's __doc__ string for more info.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Help()函数是一个内置函数，用于查看函数或模块用途的详细说明：\n",
    "import copy\n",
    "help(copy.copy)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Dir()函数也是Python内置函数，dir() 函数不带参数时，返回当前范围内的变量、方法和定义的类型列表；带参数时，返回参数的属性、方法列表。\n",
    "# import copy\n",
    "dir(copy.copy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## \\_\\_new\\_\\_; \\_\\_init\\_\\_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# __init__方法负责对象的初始化，系统执行该方法前，其实该对象已经存在了\n",
    "# 只能返回 None 值，否则报错\n",
    "class A:\n",
    "    def __init__(self):\n",
    "        print(\"__init__ \")\n",
    "        super(A, self).__init__()\n",
    "\n",
    "    def __new__(cls):\n",
    "        print(\"__new__ \")\n",
    "        return super(A, cls).__new__(cls)\n",
    "\n",
    "    def __call__(self):  # 可以定义任意参数\n",
    "        print('__call__ ')\n",
    "\n",
    "A()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 一般我们不会去重写该方法，除非你确切知道怎么做，\n",
    "# 它作为构造函数用于创建对象，是一个工厂函数，专用于生产实例对象。\n",
    "# 著名的设计模式之一，单例模式，就可以通过此方法来实现。\n",
    "class BaseController(object):\n",
    "    _singleton = None\n",
    "    def __new__(cls, *a, **k):\n",
    "        if not cls._singleton:\n",
    "            cls._singleton = object.__new__(cls, *a, **k)\n",
    "        return cls._singleton\n",
    "# 段代码出自 https://github.com/bottlepy/bottle/blob/release-0.6/bottle.py\n",
    "# 这就是通过 __new__ 方法是实现单例模式的的一种方式，如果实例对象存在了就直接返回该实例即可，如果还没有，那么就先创建一个实例，再返回。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 在动态检查对象是否包含某些属性（包括方法〉相关的函数\n",
    "    1. hasattr(obj, name)：检查 obj 对象是否包含名为 name 的属性或方法。\n",
    "    2. getattr(object, name[, default])：获取 object 对象中名为 name 的属性的属性值。\n",
    "    3. setattr(obj, name, value，/)：将obj 对象的 name 属性设为 value。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "True\n",
      "True\n",
      "疯狂Python讲义很不错\n",
      "20\n",
      "天气不错\n",
      "32\n"
     ]
    }
   ],
   "source": [
    "class Comment:\n",
    "    def __init__ (self, detail, view_times):\n",
    "        self.detail = detail\n",
    "        self.view_times = view_times\n",
    "    def info ():\n",
    "        print(\"一条简单的评论，内容是%s\" % self.detail)\n",
    "       \n",
    "c = Comment('疯狂Python讲义很不错', 20)\n",
    "# 判断是否包含指定的属性或方法\n",
    "print(hasattr(c, 'detail')) # True\n",
    "print(hasattr(c, 'view_times')) # True\n",
    "print(hasattr(c, 'info')) # True\n",
    "# 获取指定属性的属性值\n",
    "print(getattr(c, 'detail')) # '疯狂Python讲义很不错'\n",
    "print(getattr(c, 'view_times')) # 20\n",
    "# 由于info是方法，故下面代码会提示：name 'info' is not defined\n",
    "#print(getattr(c, info, '默认值'))\n",
    "# 为指定属性设置属性值\n",
    "setattr(c, 'detail', '天气不错')\n",
    "setattr(c, 'view_times', 32)\n",
    "# 输出重新设置后的属性值\n",
    "print(c.detail)\n",
    "print(c.view_times)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 负索引 \n",
    "    从右侧开始索引"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 从右边开始检索,能用于列表中的切片\n",
    "mylist[-3]\n",
    "mylist[-6:-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 如何以就地操作方式打乱一个列表的元素 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2, 4, 7, 8, 6, 0, 3, 1, 5]"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mylist=[0,1,2,3,4,5,6,7,8]\n",
    "from random import shuffle\n",
    "shuffle(mylist)\n",
    "mylist"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Closure （闭包）\n",
    "    当一个嵌套函数在其外部区域引用了一个值时，该嵌套函数就是一个闭包。其意义就是会记录这个值。\n",
    "    参考：https://zhuanlan.zhihu.com/p/57874441\n",
    "    它是一种高阶函数，并且外层函数（例子中的add_num）将其内部定义的函数（add）作为返回值返回，同时由于返回的内层函数扩展了外层函数的环境（environment），也就是对其产生了一个引用，那么在调用返回的内部函数（add5）的时候，能够引用到其（add）定义时的外部环境（在例子中，即 a 的值）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "9"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def add_num(a):\n",
    "    def add(b): # 嵌套定义\n",
    "        return a + b \n",
    "    return add # 作为函数的返回值\n",
    "\n",
    "addNum = add_num(5)\n",
    "addNum(4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n"
     ]
    }
   ],
   "source": [
    ">>> def A(x):\n",
    "    def B(y):\n",
    "        print(y)\n",
    "    return B\n",
    "# >>> A(7)()\n",
    ">>> A(7)\n",
    ">>> A(4)(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# for循环中的lambda与闭包\n",
    "# https://www.jianshu.com/p/84f3e0f4d218\n",
    "def funx():\n",
    "    return [lambda x : i*x for i in range(0,4)]\n",
    "[0,1,2,3]\n",
    "# ss = [fun(2) for fun in funx()]\n",
    "for fun in funx():\n",
    "    print(fun)\n",
    "    print(fun(2)) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 有多少种运算符？解释一下算数运算符。\n",
    "    在Python中，我们有7种运算符：算术运算符、关系运算符、赋值运算符、逻辑运算符、位运算符、成员运算符、身份运算符。\n",
    "\n",
    "    有7个算术运算符，能让我们对数值进行算术运算: +,-,*,/,%,//,/;\n",
    "\n",
    "    逻辑运算符: and,or,not\n",
    "\n",
    "    身份运算符: is’和‘is not’，我们可以确认两个值是否相同。\n",
    "\n",
    "    位运算符: 与（&），按位与运算符：参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0\n",
    "            或（|），按位或运算符：只要对应的二个二进位有一个为1时，结果位就为1。\n",
    "            异或（^），按位异或运算符：当两对应的二进位相异时，结果为1\n",
    "            取反（~），按位取反运算符：对数据的每个二进制位取反,即把1变为0,把0变为1\n",
    "            左位移（<<），运算数的各二进位全部左移若干位，由 << 右边的数字指定了移动的位数，高位丢弃，低位补0\n",
    "            右位移（>>），把\">>\"左边的运算数的各二进位全部右移若干位，>> 右边的数字指定了移动的位数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## //，%和 ** 运算符\n",
    "    //运算符执行地板除法（向下取整除），它会返回整除结果的整数部分。\n",
    "    ** 执行取幂运算。a**b会返回a的b次方。\n",
    "    % 执行取模运算，返回除法的余数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 集合（set）运算\n",
    "    子集⊆、真子集⊂： set的运算<对应于真子集⊂，<=对应于子集⊆，对应set类型的内置函数issubset()\n",
    "    超集/包含关系⊇、 ⊃：set的运算>对应于真包含⊃，>=对应于包含⊇，对应的内置函数是issuperset()\n",
    "    不相交集: 一个集合中的任何一个元素都不属于另一个集合，可以说这两个集合是不相交集（Disjoint sets），也就是说，交集为空 。判断函数是isdisjoint()\n",
    "    两集合的交集: set 的交集 的运算符号是&，采用这个符号是显然的，因为交集运算与位与（bit-wise AND）运算相似。对应的内置函数是intersection()\n",
    "    两集合的并集: set 的并集的运算符号是|，采用这个符号也是显然的，因为并集运算与位或（bit-wise OR）运算相似。对应的内置函数是union()\n",
    "    差集（减法）运算: set的差集运算，也就是从一个集合里减去另一个集合的所有元素，很直接的用减号表示，内置函数是difference()\n",
    "    对称差集（异或）运算: 数学上，两个集合的对称差(Symmetric difference)是只属于其中一个集合，而不被两个集合同时包含。 例如：集合{1,2,3}和{3,4}的对称差为{1,2,4}。集合论中的这个运算相当于布尔逻辑中的异或运算。所以在Python里使用了异或的符号（^）表示，内置函数为symmetric_difference()\n",
    "    \n",
    "### 集合内置函数的几个特点\n",
    "\n",
    "    集合内置函数里，有三个判断函数（is开头的函数）和四个运算函数（intersection, union, difference和symmetric_difference），表示运算的函数有下面几个特点：\n",
    "    1. 可以传递多个参数，表示连续运算\n",
    "    2. 可以传递除集合外的其他可递归类型（iterable） "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 108,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    ">>> A = {1, 2, 3}\n",
    ">>> B = {1, 2, 3, 4, 5}\n",
    ">>> A <= B  # 判断是否子集\n",
    ">>> A < B  # 判断是否真子集\n",
    ">>> A <= A\n",
    ">>> B < A\n",
    ">>> A.issubset(B)  # set类型的内置函数issubset()同样可以判断是否子集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 109,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    ">>> A = {1, 2, 3}\n",
    ">>> B = {1, 2, 3, 4, 5}\n",
    ">>> B >= A\n",
    ">>> B > A\n",
    ">>> A >= A\n",
    ">>> A > B # A 包含 B ？\n",
    ">>> A.issuperset(B) # A 包含 B ？\n",
    ">>> B.issuperset(A) \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 119,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    ">>> A = {1, 2, 3}\n",
    ">>> B = {1, 2, 3, 4, 5}\n",
    ">>> C = {9,8,7}\n",
    ">>> A.isdisjoint(B) # 判断是否不相交\n",
    "False\n",
    ">>> B.isdisjoint(C)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 116,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{4, 5}"
      ]
     },
     "execution_count": 116,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    ">>> A = {1, 2, 3, 4, 5}\n",
    ">>> B = {4, 5, 6, 7, 8}\n",
    ">>> A & B  # 获得交集\n",
    "set([4, 5])\n",
    ">>> A.intersection(B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 115,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{1, 2, 3, 4, 5, 6, 7, 8}"
      ]
     },
     "execution_count": 115,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    ">>> A = {1, 2, 3, 4, 5}\n",
    ">>> B = {4, 5, 6, 7, 8}\n",
    ">>> A | B # 直接或\n",
    "set([1, 2, 3, 4, 5, 6, 7, 8])\n",
    ">>> A.union(B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{1, 2, 3}"
      ]
     },
     "execution_count": 114,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    ">>> A = {1, 2, 3, 4, 5}\n",
    ">>> B = {4, 5, 6, 7, 8}\n",
    ">>> A - B  # 差集，直接做减法\n",
    "set([1, 2, 3])\n",
    ">>> A.difference(B)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{1, 2, 3, 6, 7, 8}"
      ]
     },
     "execution_count": 113,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    ">>> A = {1, 2, 3, 4, 5}\n",
    ">>> B = {4, 5, 6, 7, 8}\n",
    ">>> A ^ B\n",
    "set([1, 2, 3, 6, 7, 8])\n",
    ">>> A.symmetric_difference(B) # 两个集合的对称差\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'a', 'b', 'd', 'e', 'f'}"
      ]
     },
     "execution_count": 111,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    ">>> A = {1, 2, 3, 4, 5}\n",
    ">>> B = {4, 5, 6, 7, 8}\n",
    ">>> C = {4, 5, 9, 0}\n",
    ">>> A.intersection(B, C)    #连续交集运算\n",
    "set([4, 5])\n",
    ">>> A & B & C    #连续交集运算\n",
    "set([4, 5])\n",
    ">>> \n",
    ">>> A = [1, 2, 3, 4, 5]\n",
    ">>> B = [4, 5, 6, 7, 8]\n",
    ">>> set(A).union(B)    #和list作并集\n",
    "set([1, 2, 3, 4, 5, 6, 7, 8])\n",
    ">>> set('abc').symmetric_difference('cdef')    #字符串也是sequence的一种\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "25\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "print( 5//2)\n",
    "print(5**2)\n",
    "print(5%2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 元组的解封装"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "12"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    ">>> mytuple=3,4,5\n",
    ">>> mytuple\n",
    "(3, 4, 5)\n",
    "# 现在我们将这些值解封装到变量 x，y，z 中：\n",
    ">>> x,y,z=mytuple\n",
    ">>> x+y+z\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n"
     ]
    }
   ],
   "source": [
    "# 三元运算\n",
    "y = 1\n",
    "x = 'a' if y >1 else 2\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[<function <listcomp>.<lambda> at 0x7f9fd0605e18>, <function <listcomp>.<lambda> at 0x7f9fd3684268>, <function <listcomp>.<lambda> at 0x7f9fd05a89d8>, <function <listcomp>.<lambda> at 0x7f9fd05a8730>]\n"
     ]
    }
   ],
   "source": [
    "# 匿名函数: lambda\n",
    "x = [lambda x: x +2  for x in [1,2,3,4]]\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 管理内存\n",
    "\n",
    "**Python 中垃圾回收机制: 引用计数(主要), 标记清除, 分代收集(辅助)**\n",
    "\n",
    "\n",
    "Python中，主要依靠gc（garbage collector）模块的引用计数技术来进行垃圾回收。所谓引用计数，就是考虑到Python中变量的本质不是内存中一块存储数据的区域，而是对一块内存数据区域的引用。所以python可以给所有的对象（内存中的区域）维护一个引用计数的属性，在一个引用被创建或复制的时候，让python,把相关对象的引用计数+1；相反当引用被销毁的时候就把相关对象的引用计数-1。当对象的引用计数减到0时，自然就可以认为整个python中不会再有变量引用这个对象，所以就可以把这个对象所占据的内存空间释放出来了。\n",
    "\n",
    "引用计数技术在每次引用创建和销毁时都要多做一些操作，这可能是一个小缺点，当创建和销毁很频繁的时候难免带来一些效率上的不足。但是其最大的好处就是实时性，其他语言当中，垃圾回收可能只能在一些固定的时间点上进行，比如当内存分配失败的时候进行垃圾回收，而引用计数技术可以动态地进行内存的管理。\n",
    "\n",
    "一、变量与对象\n",
    "\n",
    "    1、变量，通过变量指针引用对象\n",
    "\n",
    "    变量指针指向具体对象的内存空间，取对象的值。\n",
    "\n",
    "    2、对象，类型已知，每个对象都包含一个头部信息（头部信息：类型标识符和引用计数器）\n",
    "\n",
    "    注意：变量名没有类型，类型属于对象（因为变量引用对象，所以类型随对象），变量引用什么类型的对象，变量就是什么类型的。\n",
    "    \n",
    "    3、引用所指判断\n",
    "\n",
    "    通过is进行引用所指判断，is是用来判断两个引用所指的对象是否相同。\n",
    "\n",
    "        1、Python缓存了整数和短字符串，因此每个对象在内存中只存有一份，引用所指对象就是相同的，即使使用赋值语句，也只是创造新的引用，而不是对象本身；\n",
    "        2、Python没有缓存长字符串、列表及其他对象，可以由多个相同的对象，可以使用赋值语句创建出新的对象。\n",
    "        \n",
    "*引用计数法有很明显的优点：*\n",
    "    + 高效\n",
    "    + 运行期没有停顿 可以类比一下Ruby的垃圾回收机制，也就是 实时性：一旦没有引用，内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处：处理回收内存的时间分摊到了平时。\n",
    "    + 对象有确定的生命周期\n",
    "    + 易于实现\n",
    "    \n",
    "*原始的引用计数法也有明显的缺点：*\n",
    "\n",
    "    + 维护引用计数消耗资源，维护引用计数的次数和引用赋值成正比，而不像mark and sweep等基本与回收的内存数量有关。\n",
    "    无法解决循环引用的问题。A和B相互引用而再没有外部引用A与B中的任何一个，它们的引用计数都为1，但显然应该被回收。\n",
    "\n",
    "* 循环引用问题：\n",
    "\n",
    "    当Python中的对象越来越多，占据越来越大的内存，启动垃圾回收(garbage collection)，将没用的对象清除。\n",
    "    为了解决这两个致命弱点，Python又引入了以下两种GC机制。\n",
    "\n",
    "**标记-清除的回收机制**\n",
    "\n",
    "　　针对循环引用这个问题，比如有两个对象互相引用了对方，当外界没有对他们有任何引用，也就是说他们各自的引用计数都只有1的时候，如果可以识别出这个循环引用，把它们属于循环的计数减掉的话，就可以看到他们的真实引用计数了。基于这样一种考虑，有一种方法，比如从对象A出发，沿着引用寻找到对象B，把对象B的引用计数减去1；然后沿着B对A的引用回到A，把A的引用计数减1，这样就可以把这层循环引用关系给去掉了。\n",
    "\n",
    "不过这么做还有一个考虑不周的地方。假如A对B的引用是单向的， 在到达B之前我不知道B是否也引用了A，这样子先给B减1的话就会使得B称为不可达的对象了。为了解决这个问题，python中常常把内存块一分为二，将一部分用于保存真的引用计数，另一部分拿来做为一个引用计数的副本，在这个副本上做一些实验。比如在副本中维护两张链表，一张里面放不可被回收的对象合集，另一张里面放被标记为可以被回收（计数经过上面所说的操作减为0）的对象，然后再到后者中找一些被前者表中一些对象直接或间接单向引用的对象，把这些移动到前面的表里面。这样就可以让不应该被回收的对象不会被回收，应该被回收的对象都被回收了。\n",
    "\n",
    "**分代回收**\n",
    "\n",
    "　　分代回收策略着眼于提升垃圾回收的效率。研究表明，任何语言，任何环境的编程中，对于变量在内存中的创建/销毁，总有频繁和不那么频繁的。比如任何程序中总有生命周期是全局的、部分的变量。\n",
    "Python将所有的对象分为0，1，2三代；\n",
    "所有的新建对象都是0代对象；\n",
    "当某一代对象经历过垃圾回收，依然存活，就被归入下一代对象。\n",
    "\n",
    "gc模块提供一个接口给开发者设置垃圾回收的选项。上面说到，采用引用计数的方法管理内存的一个缺陷是循环引用，而gc模块的一个主要功能就是解决循环引用的问题。\n",
    "\n",
    "* 参考：Python内存管理和垃圾回收：https://zhuanlan.zhihu.com/p/55601173\n",
    "        Python 内存管理: https://wxnacy.com/2019/06/16/python-memory-management/\n",
    "      "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## __call__, __import__ "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__new__ \n",
      "__init__ \n",
      "True\n",
      "a是实例对象，同时还是可调用对象，那么我就可以像函数一样调用它。\n",
      "__call__ \n"
     ]
    }
   ],
   "source": [
    "# 关于 __call__ 方法，不得不先提到一个概念，就是可调用对象（callable），我们平时自定义的函数、内置函数和类都属于可调用对象，\n",
    "# 但凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象，判断对象是否为可调用对象可以用函数 callable\n",
    "# 如果在类中实现了 __call__ 方法，那么实例对象也将成为一个可调用对象，我们回到最开始的那个例子：\n",
    "class A:\n",
    "    def __init__(self):\n",
    "        print(\"__init__ \")\n",
    "        super(A, self).__init__()\n",
    "\n",
    "    def __new__(cls):\n",
    "        print(\"__new__ \")\n",
    "        return super(A, cls).__new__(cls)\n",
    "\n",
    "    def __call__(self):  # 可以定义任意参数\n",
    "        print('__call__ ')\n",
    "        \n",
    "a = A()\n",
    "print(callable(a))  # True\n",
    "\n",
    "# a是实例对象，同时还是可调用对象，那么我就可以像函数一样调用它。试试：\n",
    "print(\"a是实例对象，同时还是可调用对象，那么我就可以像函数一样调用它。\")\n",
    "a()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n"
     ]
    }
   ],
   "source": [
    "# 实例对象也可以像函数一样作为可调用对象来用，那么，这个特点在什么场景用得上呢？这个要结合类的特性来说，\n",
    "# 类可以记录数据（属性），而函数不行（闭包某种意义上也可行），利用这种特性可以实现基于类的装饰器，在类里面记录状态，比如，下面这个例子用于记录函数被调用的次数：\n",
    "class Counter:\n",
    "    def __init__(self, func):\n",
    "        self.func = func\n",
    "        self.count = 0\n",
    "\n",
    "    def __call__(self, *args, **kwargs):\n",
    "        self.count += 1\n",
    "        return self.func(*args, **kwargs)\n",
    "\n",
    "@Counter\n",
    "def foo():\n",
    "    pass\n",
    "\n",
    "for i in range(10):\n",
    "    foo()\n",
    "\n",
    "print(foo.count)  # 10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# __import__\n",
    "# 　　1. 函数功能用于动态的导入模块，主要用于反射或者延迟加载模块。\n",
    "# 　　2. __import__(module)相当于import module\n",
    "#mian.py\n",
    "print ('main')\n",
    "\n",
    "index = __import__('index')\n",
    "dir(index)\n",
    "index.sayHello()\n",
    "index.sayHelloZhCn()\n",
    "\n",
    "# 执行main.py，可以证实动态加载了index.py，__import__返回的模块也是index模块"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#index.py\n",
    "\n",
    "print ('index')\n",
    "\n",
    "def sayHello():\n",
    "    print('hello index')\n",
    "\n",
    "def sayHelloZhCn():\n",
    "    print('你好 index')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 对象变动 Mutation\n",
    "    Python中可变(mutable)与不可变(immutable)的数据类型让新手很是头痛。简单的说，可变(mutable)意味着\"可以被改动\"，而不可变(immutable)的意思是“常量(constant)”。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['hi']\n",
      "['hi', 'bye']\n",
      "['hi', 'bye']\n"
     ]
    }
   ],
   "source": [
    "foo = ['hi']\n",
    "print(foo)\n",
    "# Output: ['hi']\n",
    "\n",
    "bar = foo\n",
    "bar += ['bye']\n",
    "\n",
    "print(foo)\n",
    "# Output: ['hi']\n",
    "\n",
    "print(bar)\n",
    "# Output: ['hi', 'bye']\n",
    "\n",
    "# 这不是一个bug。这是对象可变性(mutability)在作怪。\n",
    "# 每当你将一个变量赋值为另一个可变类型的变量时，对这个数据的任意改动会同时反映到这两个变量上去。\n",
    "# 新变量只不过是老变量的一个别名而已。这个情况只是针对可变数据类型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0]\n",
      "[1, 1]\n",
      "[0, 2]\n"
     ]
    }
   ],
   "source": [
    "# 赋值与地址引用\n",
    "def fun(x=0,y=[]):\n",
    "    y.append(x)\n",
    "    return y\n",
    "\n",
    "print(fun())\n",
    "print(fun(1,[1]))\n",
    "print(fun(2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 猴子补丁 (Monkey Patch)\n",
    "\n",
    "属性在运行时的动态替换，叫做猴子补丁（Monkey Patch）。\n",
    "\n",
    "**其实这根本的原因在于Python语法的灵活性，方法可以像普通对象那样使用。**\n",
    "\n",
    "* 名称由来：\n",
    "\n",
    "    1. 这个词原来为Guerrilla Patch，杂牌军、游击队，说明这部分不是原装的，在英文里guerilla发音和gorllia(猩猩)相似，再后来就写了monkey(猴子)。\n",
    "    2. 还有一种解释是说由于这种方式将原来的代码弄乱了(messing with it)，在英文里叫monkeying about(顽皮的)，所以叫做Monkey Patch。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "参考：[StackOverflow](https://stackoverflow.com/questions/5626193/what-is-monkey-patching)\n",
    "\n",
    "No, it's not like any of those things. It's simply the dynamic replacement of attributes at runtime.\n",
    "\n",
    "For instance, consider a class that has a method get_data. This method does an external lookup (on a database or web API, for example), and various other methods in the class call it. However, in a unit test, you don't want to depend on the external data source - so you dynamically replace the get_data method with a stub that returns some fixed data.\n",
    "\n",
    "Because Python classes are mutable, and methods are just attributes of the class, you can do this as much as you like - and, in fact, you can even replace classes and functions in a module in exactly the same way.\n",
    "\n",
    "But, as a commenter pointed out, use caution when monkeypatching:\n",
    "\n",
    "    If anything else besides your test logic calls get_data as well, it will also call your monkey-patched replacement rather than the original -- which can be good or bad. Just beware.\n",
    "\n",
    "    If some variable or attribute exists that also points to the get_data function by the time you replace it, this alias will not change its meaning and will continue to point to the original get_data. (Why? Python just rebinds the name get_data in your class to some other function object; other name bindings are not impacted at all.)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hi\n",
      "Hi, monkey\n"
     ]
    }
   ],
   "source": [
    "class A:\n",
    "    def func(self):\n",
    "        print(\"Hi\")\n",
    "    def monkey(self):\n",
    "        print(\"Hi, monkey\")\n",
    "\n",
    "a = A()\n",
    "a.func()\n",
    "\n",
    "class A:\n",
    "    def func(self):\n",
    "        print(\"Hi\")\n",
    "    def monkey(self):\n",
    "        print(\"Hi, monkey\")\n",
    "a = A()\n",
    "A.func=A.monkey   #在运行的时候，才改变了func\n",
    "a.func()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## monkey patch的应用场景\n",
    "\n",
    "这里有一个比较实用的例子，很多代码用到 import json，后来发现ujson性能更高，如果觉得把每个文件的import json 改成 import ujson as json成本较高，或者说想测试一下用ujson替换json是否符合预期:(*也可以考虑类继承*)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json  \n",
    "import ujson  \n",
    "\n",
    "def monkey_patch_json():  \n",
    "    json.__name__ = 'ujson'  \n",
    "    json.dumps = ujson.dumps  \n",
    "    json.loads = ujson.loads  \n",
    "\n",
    "monkey_patch_json()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## lambda表达式\n",
    "    lambda表达式是一行函数。\n",
    "    它们在其他语言中也被称为匿名函数。如果你不想在程序中对一个函数使用两次，你也许会想用lambda表达式，它们和普通的函数完全一样。\n",
    "**原型**\n",
    "   lambda 参数:操作数（参数）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add = lambda x,y: y+x\n",
    "print(add(2,3))\n",
    "\n",
    "# 排序\n",
    "a = [(1, 2), (4, 1), (9, 10), (13, -3)]\n",
    "a.sort(key=lambda x: x[1])\n",
    "\n",
    "print(a)\n",
    "# Output: [(13, -3), (4, 1), (1, 2), (9, 10)]\n",
    "\n",
    "\n",
    "# 列表并行排序\n",
    "list1 = [1,2,3,4,7,8,23]\n",
    "list2 = [34,2,67,7,5,9,87]\n",
    "data = zip(list1, list2)\n",
    "data = sorted(data)\n",
    "list1, list2 = map(lambda t: list(t), zip(*data))\n",
    "print(list1,list2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 简单问题示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "鸡：23 兔：12\n"
     ]
    }
   ],
   "source": [
    "# 农场里有鸡和兔子，总共有 35个脑袋和 94条腿，计算一下兔子和鸡分别有多少只？\n",
    "for i in range(1,35+1):\n",
    "    if 2*i + 4*(35-i) == 94:\n",
    "        print(f\"鸡：{i} 兔：{35-i}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, 6, 45, 56, 67, 89, 90, 90, 120]\n"
     ]
    }
   ],
   "source": [
    "# 合并两个有序列表\n",
    "x = [6,56,67,89,90]\n",
    "y = [1,2,3,45,90,120]\n",
    "\n",
    "# 原list就地修改\n",
    "def merge_order_list(l1,l2):\n",
    "    if l1[-1] <= l2[0]:\n",
    "        rst = l1 + l2\n",
    "        return rst\n",
    "    elif l2[-1] <= l1[0]:\n",
    "        rst = l2 + l1 \n",
    "        return rst\n",
    "    else:\n",
    "        pass\n",
    "\n",
    "    maxList, littleList  = [], []\n",
    "    \n",
    "    if len(l1) >= len(l2):\n",
    "        maxList  = l1 \n",
    "        littleList = l2\n",
    "    else:\n",
    "        maxList  = l1 \n",
    "        littleList = l2   \n",
    "    for i in range(0, len(maxList)):\n",
    "        for j in range(0,len(littleList)-1):\n",
    "            if littleList[j] <= maxList[i]:\n",
    "                maxList = maxList[0:i] + [littleList[j]] + maxList[i:]\n",
    "                littleList.remove(littleList[j])\n",
    "    if littleList:\n",
    "        maxList.extend(littleList)\n",
    "    return maxList\n",
    "\n",
    "# 使用归并排序中的 merge 过程\n",
    "def merge(l1,l2):\n",
    "    len1 = len(l1)\n",
    "    len2 = len(l2)\n",
    "    i,j = 0, 0\n",
    "    rst = []\n",
    "    while i <len1 and j < len2:\n",
    "        if l1[i] <= l2[j]:\n",
    "            rst.append(l1[i])\n",
    "            i += 1\n",
    "        else:\n",
    "            rst.append(l2[j])\n",
    "            j += 1  \n",
    "\n",
    "    if i == len1:\n",
    "        rst.extend(l2[j:])\n",
    "    else:\n",
    "        rst.extend(l1[i:])\n",
    "    return rst\n",
    "\n",
    "# print(merge_order_list(x,y))\n",
    "print(merge(x,y))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(True, '1 is 2 `s powers  0')\n"
     ]
    }
   ],
   "source": [
    "# 检查给定数字n是否为2或0的幂\n",
    "# 0 的任何次幂都为0；其实0没有次幂的问题\n",
    "import math\n",
    "def check_log(n):\n",
    "    if n == 0 :\n",
    "        return (True, f\"{n} is 0 `s powers\" )\n",
    "    if n < 0 :\n",
    "        return None\n",
    "    power = math.log(n,2)\n",
    "    if power == 0:\n",
    "        return (True, f\"{n} is 2 `s powers  0\" )\n",
    "    elif power > 1:\n",
    "        return (True, f\"{n} is 2 `s power {power}\")\n",
    "                \n",
    "print(check_log(1))\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 apple\n",
      "2 banana\n",
      "3 grapes\n",
      "4 pear\n"
     ]
    }
   ],
   "source": [
    "# 枚举 (enumerate)\n",
    "my_list = ['apple', 'banana', 'grapes', 'pear']\n",
    "for index, value in enumerate(my_list,1): # index从 1 开始\n",
    "    print(index, value)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 可以直接引入enmu 模块\n",
    "from enum import Enum\n",
    "# 定义Season枚举类\n",
    "Season = Enum('Season', ('SPRING', 'SUMMER', 'FALL', 'WINTER'))\n",
    "\n",
    "# 定义枚举类\n",
    "# 定义枚举时，成员名不允许重复\n",
    "# 成员值允许相同，第二个成员的名称被视作第一个成员的别名 \n",
    "class Color(Enum):\n",
    "    red = 1\n",
    "    green = 2\n",
    "    blue = 3\n",
    "    \n",
    "# 枚举成员有值（默认可重复），枚举成员具有友好的字符串表示：  \n",
    "\n",
    "print(Color.red)              # Color.red\n",
    "print(Color.blue)             # Color.red\n",
    "print(Color.red is Color.blue)# True\n",
    "print(Color(1))               # Color.red  在通过值获取枚举成员时，只能获取到第一个成员"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 若要不能定义相同的成员值，可以通过 unique 装饰\n",
    "from enum import Enum, unique\n",
    "@unique\n",
    "class Color(Enum):\n",
    "    red   = 1\n",
    "    green = 2\n",
    "    blue  = 1  # ValueError: duplicate values found in <enum 'Color'>: blue -> red\n",
    "\n",
    "\"\"\"枚举取值\n",
    "# 可以通过成员名来获取成员也可以通过成员值来获取成员: \"\"\"\n",
    "print(Color['red'])  # Color.red  通过成员名来获取成员\n",
    "print(Color(1))      # Color.red  通过成员值来获取成员\n",
    "\n",
    "\"\"\"每个成员都有名称属性和值属性\"\"\"\n",
    "member = Color.red\n",
    "print(member.name)   # red\n",
    "print(member.value)  # 1\n",
    "\n",
    "# 支持迭代的方式遍历成员，按定义的顺序，如果有值重复的成员，只获取重复的第一个成员：\n",
    "for color in Color:\n",
    "    print(color)\n",
    "\n",
    "# 特殊属性 __members__ 是一个将名称映射到成员的有序字典，也可以通过它来完成遍历：\n",
    "for color in Color.__members__.items():\n",
    "    print(color)          # ('red', <Color.red: 1>)\n",
    "    \n",
    "\"\"\"枚举比较\n",
    "枚举的成员可以通过 is 同一性比较或通过 == 等值比较：\"\"\"\n",
    "\n",
    "Color.red is Color.red\n",
    "Color.red is not Color.blue\n",
    "\n",
    "Color.blue == Color.red\n",
    "Color.blue != Color.red\n",
    "\n",
    "# 枚举成员不能进行大小比较：\n",
    "Color.red < Color.blue # TypeError: unorderable types: Color() < Color()\n",
    "\n",
    "\"\"\"扩展枚举 IntEnum\n",
    "# IntEnum 是 Enum 的扩展，不同类型的整数枚举也可以相互比较：\"\"\"\n",
    "\n",
    "from enum import IntEnum\n",
    "class Shape(IntEnum):\n",
    "    circle = 1\n",
    "    square = 2\n",
    "\n",
    "class Request(IntEnum):\n",
    "    post = 1\n",
    "    get = 2\n",
    "\n",
    "print(Shape.circle == 1)            # True\n",
    "print(Shape.circle < 3)             # True\n",
    "print(Shape.circle == Request.post) # True\n",
    "print(Shape.circle >= Request.post) # True\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 递归 斐波那契\n",
    "# 走楼梯问题：\n",
    "# 楼梯有n阶台阶，上楼可以一步上1阶,2阶，编程序计算共有多少种不同的走法？\n",
    "def up_stairs(n):\n",
    "    if n <= 1:\n",
    "        return 1\n",
    "    elif n == 2:\n",
    "        return 2\n",
    "    else:\n",
    "        return up_stairs(n-1) + up_stairs(n-2)\n",
    "for n in range(2,10):\n",
    "    print(up_stairs(n), end=\" \")\n",
    "    \n",
    "# # 使用生成器 yield\n",
    "def fab(max):\n",
    "    if max <= 2:\n",
    "        return 1\n",
    "    n, a, b = 0, 0, 1\n",
    "    while n < max:\n",
    "        yield b\n",
    "        a, b = b, a + b\n",
    "        n = n + 1\n",
    "\n",
    "for ni in fab(20):\n",
    "    print (ni, end=\" \")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 尾递归 阶乘\n",
    "\n",
    "def fabs(n, a):\n",
    "    if n <= 1 :\n",
    "        return a \n",
    "    else:\n",
    "        return n*fabs(n-1,a)\n",
    "    \n",
    "print(fabs(4,1))\n",
    "\n",
    "# 尾递归 斐波那契 数列\n",
    "def fibs(n,a,b):\n",
    "    if n <= 2:\n",
    "        return b\n",
    "    else:\n",
    "        return fibs(n-1,b, a+b)\n",
    "for i in range(1,21):    \n",
    "    print(fibs(i,1,1),end=\" \")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 快速排序\n",
    "def quick_sort(listA):\n",
    "    length = len(listA)\n",
    "    if length <= 1:\n",
    "        return listA\n",
    "    else:\n",
    "        pivot = listA[0]\n",
    "        # import random\n",
    "        # pivot = random.choice(listA)\n",
    "        greater = [element for element in listA[1:] if element > pivot]\n",
    "        lesser = [element for element in listA[1:] if element <= pivot]\n",
    "        return quick_sort(lesser) + [pivot] + quick_sort(greater)\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    listA = [0, 5, 3, 2, 2]\n",
    "    sortedList = quick_sort(listA)\n",
    "    print(sortedList)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 2, 2, 3, 5]\n"
     ]
    }
   ],
   "source": [
    "# 归并排序\n",
    "def merge_sort_fast(listA):\n",
    "    start = []\n",
    "    end = []\n",
    "    while len(listA) > 1:\n",
    "        a = min(listA)\n",
    "        b = max(listA)\n",
    "        start.append(a)\n",
    "        end.append(b)\n",
    "        listA.remove(a)\n",
    "        listA.remove(b)\n",
    "    if listA:\n",
    "        start.append(listA[0])\n",
    "    end.reverse()\n",
    "    return start + end\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    listA = [0, 5, 3, 2, 2]\n",
    "    sortedList = merge_sort_fast(listA)\n",
    "    print(sortedList)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 二分查找，基于有序列表\n",
    "\"\"\" 二分查找 \"\"\"\n",
    "\n",
    "def binary_search(listA, target):\n",
    "    left = 0\n",
    "    right = len(listA) - 1 \n",
    "    while left <= right:\n",
    "        midpoint = (left + right) // 2\n",
    "        current_item = listA[midpoint]\n",
    "        # if listA[left] == target:\n",
    "        #     return left\n",
    "        # elif listA[right] == target:\n",
    "        #     return right\n",
    "        if current_item == target:\n",
    "            return midpoint\n",
    "        else:\n",
    "            if target < current_item:\n",
    "                right = midpoint - 1\n",
    "            else:\n",
    "                left = midpoint + 1\n",
    "    return None\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    listA = [1,4,8,9,12,45,78]\n",
    "    target = 12\n",
    "    rst = binary_search(listA, target)\n",
    "    if rst:\n",
    "        print(\"OK: \",rst)\n",
    "    else:\n",
    "        print(\"Not found !\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## yield send, next\n",
    "    * 带有yield关键字的函数自动变成生成器\n",
    "    * 生成器被调用时不会立即执行\n",
    "    使用next函数获取生成器的生成的值\n",
    "    \n",
    "    1、对于生成器，当调用函数next(generator)时，将获得生成器yield后面表达式的值；\n",
    "    2、当生成器已经执行完毕时，再次调用next函数，生成器会抛出StopIteration异常\n",
    "\n",
    "    扩展：\n",
    "    1、当生成器内部执行到return语句时，自动抛出StopIteration异常，return的值将作为异常的解释\n",
    "    2、外部可以通过generator.close()函数手动关闭生成器，此后调用next或者send方法将抛出异常\n",
    "    \n",
    "    Error:不能将一个非None的值传给初始的生成器\n",
    "        在调用带非空参数的send函数之前，我们应该使用next(generator)或者send(None)使得生成器执行到yield语句并暂停。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "def func(n):\n",
    "    for i in range(0, n):\n",
    "        print('func: ', i)\n",
    "        yield i\n",
    " \n",
    "f = func(10)\n",
    "while True:\n",
    "    print(next(f))\n",
    "    time.sleep(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# next与send函数 的区别\n",
    "import time\n",
    "def func(n):\n",
    "    for i in range(0, n):\n",
    "        arg = yield i\n",
    "        print('func:', arg)\n",
    " \n",
    "f = func(10)\n",
    "while True:\n",
    "    print('main:', next(f))\n",
    "    print('main:', f.send(100))\n",
    "    time.sleep(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# yield from 生成器嵌套\n",
    "# Python3.3之后引入的新语法：\n",
    "# 参考：https://www.jianshu.com/p/87da832730f5；http://flupy.org/resources/yield-from.pdf\n",
    "# 暂无打开双向通道案例，把最外层的调用方与最内层的子生成器连接起来，这样二者可以直接发送和产出值，还可以直接传入异常，而不用在位于中间的协程中添加大量处理异常的样板代码。\n",
    "\n",
    ">>> def chain(*iterables):\n",
    "...     for i in iterables:\n",
    "...         yield from i\n",
    "...\n",
    ">>> list(chain(s, t))\n",
    "['A', 'B', 'C', 0, 1, 2]"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
